/**
**************************************************************************************
*Palisma - Secrets of the Illuminati is an open-source 2D RPG                        *
*Copyright (C) 2006, Tony Sparks                                                     *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
//OpenGL headers 
#ifdef WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <gl/gl.h>
#include <gl/glu.h>

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include "../kernel.h"
#include "SDL_image.h"
#include "TextureLoader.h"
#include "../shared/stringutil.h"

extern Kernel* g_kernel;

#define BYTE unsigned char


unsigned int TextureLoader::m_idcounter = 0;

TextureLoader::TextureLoader(void)
{
}

/**
==================================
Create an image
==================================
*/
Image TextureLoader::CreateImage( const std::string &s ) 
{
    return CheckInMemory( s, 1, 1);
}

/**
=======================
Create an array of images
=======================
*/
Image TextureLoader::CreateImage( const std::string &s, int row, int col )
{
    return CheckInMemory( s, row, col );
}

/** 
========================
Check to se if the image is in memory,
if not load it.
========================
*/
Image TextureLoader::CheckInMemory( const std::string &s, int row, int col )
{
    if ( loadedList.find( s ) != loadedList.end() )
    {
        return loadedList[ s ];
    } 
    else if ( LoadImage( s, row, col ) )
    {
        return imageList[ m_idcounter ];
    }
    
    g_kernel->LPrintf( ("**Failed to load image "+ s).c_str() );
    return Image();
}

/**
===================================
Free a texture from memory
===================================
*/
void TextureLoader::FreeTexture( const unsigned int id)
{
    // retrieve the image
    Image oldImage = imageList[id];

    //if ( !oldImage ) return;
    // delete it from the texture list
    glDeleteTextures(1, &oldImage.textNum );


    // now delete if from the lists
    std::map< unsigned int, Image>::iterator it = imageList.begin();
    for ( ; it != imageList.end(); ++it )
    {
        if ( it->first == id )
        {
            imageList.erase( it );
            break;
        }
    }

    std::map< std::string,  Image>::iterator it2 = loadedList.begin();
    for ( ; it2 != loadedList.end(); ++it2 )
    {
        if ( it2->first == oldImage.name )
        {
            loadedList.erase( it2 );
            break;
        }
    }

    //// delete it from memory
    //delete oldImage;
}

/**
======================
Create multiple images from one

NOTE: I had a hard time reading the file doing it the C++ way,
so I tried the C way, which worked like a charm, even though the
functions have been declared deprecated.
======================
*/
bool TextureLoader::LoadImage(const std::string &filename, int rows, int cols )
{
    StringUtil util( filename, '.' ); 
    if ( util.HasNext() )
    {
        util.GetNext();
        std::string type = util.GetNext();
        if ( type == "TGA" || type == "tga" )
        {
            return LoadTGA( filename, rows, cols );
        }
        else if ( type == "BMP" || type == "bmp" )
        {
            return LoadBMP( filename, rows, cols );
        }
    }
    return false;
}


/** Load a TGA from an archived location */
bool TextureLoader::LoadTGA(const std::string &filename, int rows, int cols)
{

    MFile file; 
    if ( file.Open( filename ) )
        return false;

    SDL_RWops* wops   = SDL_RWFromMem( file.GetRawData() , file.Size() );
    SDL_Surface* surf = IMG_LoadTGA_RW( wops );
    file.Close();

    return LoadImageToMemory( surf, filename, rows, cols );
}
/** Load a BMP from an archived location */
bool TextureLoader::LoadBMP(const std::string &filename, int rows, int cols)
{
    MFile file; 
    if ( file.Open( filename ) )
        return false;

    SDL_RWops * wops = SDL_RWFromMem( file.GetRawData() , file.Size() );
    SDL_Surface* surf = IMG_Load_RW( wops, 0 );
    file.Close();

    return LoadImageToMemory( surf, filename, rows, cols );
}

/**
======================
Create multiple images from one
======================
*/
bool TextureLoader::LoadImageToMemory(SDL_Surface* surf, const std::string &filename, int rows, int cols )
{
    // test to see if we loaded anything
    if ( !surf ) {
        surf = NULL;     
        return false;
    }
    // Our new image
    Image im;
    Image* image = &im;//new Image();
    image->data = 0;
    
    image->width        = surf->w;
    image->height       = surf->h;
    image->subWidth     = (float)(surf->w / (float)cols); // get subimage dimensions
    image->subHeight    = (float)(surf->h / (float)rows);
    image->id           = ++m_idcounter;
    image->nBPP         = surf->format->BytesPerPixel;
    image->name         = filename;

    // set dimensions for subimages
    image->rows     = rows;
    image->columns  = cols;

    int imageSize = image->width * image->height * image->nBPP;
    unsigned int colorSwap = 0;

    // allocate memory for image data
    image->data = new unsigned char[imageSize];
    memcpy(image->data, surf->pixels, imageSize);

    if ( image->data )
    {
        g_kernel->LPrintf( ("--Image loaded: "+image->name).c_str() );
    }
    unsigned char red = 0, green =0 , blue = 0;
    // change BGR to RGB so OpenGL can use the data
    for (int imageIdx = 0; imageIdx < imageSize; imageIdx += image->nBPP)
	{
		colorSwap = image->data[imageIdx]; 
		image->data[imageIdx] = image->data[imageIdx+2];
		image->data[imageIdx + 2] = colorSwap;

        red  = image->data[ imageIdx ];
        green = image->data[ imageIdx+1 ];
        blue   = image->data[ imageIdx+2 ];
        
        if ( image->nBPP == 4 && blue == 255 && red == 255 && green == 0 )     // if we have alpha
        {
            image->data[ imageIdx + 3 ] = 0;   
        }
	}

    // Generate the texture
    GenTexture( image );

    image->Delete();
    // free the surface
    SDL_FreeSurface( surf );
    return true;
}
  

/**
=========================
Generate a texture
=========================
*/
void TextureLoader::GenTexture( Image* image)
{
    // ensure no division by zero
    int texWidth = 2;
    int texHeight = 2;
    
    // find the closest power of 2 for the width and height
    // of the produced texture
    while (texWidth < image->width ) {
        texWidth *= 2;
    }
    while (texHeight < image->height ) {
        texHeight *= 2;
    }
    // Get the textures dimensions
    image->texWidth     = image->width / texWidth;
    image->texHeight    = image->height / texHeight;

    glGenTextures(1, &image->textNum);		// Create The Texture

	// bind the texture
    glBindTexture(GL_TEXTURE_2D, image->textNum);

	// Disable Antialiazing for magnification/minification
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // Use if creating mipmaps
    /*gluBuild2DMipmaps(  GL_TEXTURE_2D, image->nBPP, 
                        image->width, image->height, 
                        (image->nBPP == 3 ? GL_RGB : GL_RGBA),
                        GL_UNSIGNED_BYTE, image->data);*/
    
    glTexImage2D(GL_TEXTURE_2D, 0, (image->nBPP == 3 ? GL_RGB : GL_RGBA), 
        image->width, image->height, 0, (image->nBPP == 3 ? GL_RGB : GL_RGBA), GL_UNSIGNED_BYTE, image->data);

    // add to the list
    imageList [ image->id ] = *image;
    loadedList[ image->name ] = *image;
}

/**
 *====================================
 *  Returns the previous power of 2
 *====================================
 */
int TextureLoader::NextPowerOfTwo(int n)
{
	int answer = 0;
	while (n >>= 1)
		answer++;
	return 1 << answer;
}

TextureLoader::~TextureLoader(void)
{
    std::map< unsigned int, Image >::iterator it;
    for ( it = imageList.begin(); it != imageList.end(); ++it )
    {
        FreeTexture( it->first );
    }

}











// My written loaders


///** Load a TGA */
//bool TextureLoader::LoadTGA(const std::string &filename, int rows, int cols)
//{
//    // Our file handle
//	FILE* pFile;
//	// open the TGA file
//	pFile = fopen (filename.c_str(), "rb");
//
//	// for sake of writing it...
//	typedef unsigned char ubyte;
//	// 2 wasted bytes
//	ubyte uCharDummy;
//
//	// Ignore the first two bytes
//	fread (&uCharDummy, sizeof (ubyte), 1, pFile);
//	fread (&uCharDummy, sizeof (ubyte), 1, pFile);
//
//    // the image type ( rgb, rgba )
//	ubyte imageType;
//	// read the type
//	fread(&imageType, sizeof (unsigned char), 1, pFile);
//
//	
//	// read the image type code
//	if (imageType != 2 && imageType != 3 )
//	{
//		// we have not loaded the proper type of image
//        g_kernel->LPrintf( ("**Error Loading image: " + filename).c_str() );
//        g_kernel->LPrintf( "**Improper image code: %d", imageType );
//
//        fclose( pFile );
//		return false;
//	}
//
//    // another dummy
//	short sIntDummy;
//	// More dummy data
//	fread (&uCharDummy, sizeof (unsigned char), 1, pFile);
//	fread (&sIntDummy,  sizeof (short), 1, pFile);
//	fread (&sIntDummy,  sizeof (short), 1, pFile);
//	fread (&sIntDummy,  sizeof (short), 1, pFile);
//	fread (&sIntDummy,  sizeof (short), 1, pFile);
//
//	// Create memory for our image
//	Image* image = new Image;
//    // images dimensions
//	short int width;
//	short int height;
//
//	// now its time for important data
//    fread (&width,  sizeof (short int), 1, pFile);
//    fread (&height, sizeof (short int), 1, pFile);
//    // get the bitdepth
//    fread (&image->bitDepth, sizeof (unsigned char), 1, pFile);
//
//	// Skip past some more
//	fread (&uCharDummy, sizeof (unsigned char), 1, pFile);
//
//	image->width    = width;
//	image->height   = height;
//	image->bitDepth = image->bitDepth / 8;
//
//	// get the size of the image data
//	int imageSize = image->width * image->height * image->bitDepth;
//	image->data = 0;
//    // allocate memory for the image data
//	image->data = new ubyte[ imageSize ];
//
//	// actually read the image data
//	fread (image->data, sizeof (unsigned char), imageSize, pFile);
//
//	
//	// close off the stream
//	fclose( pFile );
//   
//	// Format our image
//	image->subWidth     = float(image->width  / rows); // get subimage dimensions
//	image->subHeight    = float(image->height / cols);
//    image->id           = ++m_idcounter;
//	image->name         = filename;
//
//    // set dimensions for subimages
//    image->rows     = rows;
//    image->columns  = cols;
//
//	unsigned char swap = 0;
//    // flip right side
//    for (int start = 0, end = imageSize-image->bitDepth; start < imageSize / 2; start += image->bitDepth, end -= image->bitDepth )
//	{
//        for ( int i = 0; i < image->bitDepth; i++ )
//        {
//            swap = image->data[ i + start ];
//            image->data[ i + start ] = image->data [ i + end ];
//            image->data[ i + end ] = swap;
//        }
//    }
//
// //   // no reflection
// //   swap = 0;
// //   for (int row = 0; row < imageSize ; row += image->height*image->bitDepth )
//	//{
// //       for ( int start = row, 
// //                 end = row + (image->width*image->bitDepth) -image->bitDepth; 
// //                 start <  (image->width * image->bitDepth) / 2; 
// //                 start += image->bitDepth, 
// //                 end   -= image->bitDepth 
// //           )
// //       {
// //           for ( int i = 0; i < image->bitDepth; i++ )
// //           {
// //               swap = image->data[ i + start ];
// //               image->data[ i + start ] = image->data [ i + end ];
// //               image->data[ i + end ] = swap;
// //           }
// //       }
// //   }
//
//    unsigned char colorSwap = 0;
//    // change BGR to RGB so OpenGL can use the data
//	for (int imageIdx = 0; imageIdx < imageSize; imageIdx += image->bitDepth)
//	{
//		colorSwap = image->data[imageIdx];
//		image->data[imageIdx] = image->data[imageIdx+2];
//		image->data[imageIdx + 2] = colorSwap;
//	}
//
//    // Generate the texture
//    GenTexture( image );
//	// delete the memory
//	image->Delete();
//    return true;
//}
//
///** Load a BMP */
//bool TextureLoader::LoadBMP(const std::string &file, int rows, int cols )
//{
//	BITMAPFILEHEADER fHeader;
//	BITMAPINFOHEADER iHeader;
//    FILE *pFile;
//
//    pFile = fopen( file.c_str(), "rb" );
//
//    fread ( &fHeader, sizeof (BITMAPFILEHEADER), 1, pFile);
//    fread ( &iHeader, sizeof (BITMAPINFOHEADER), 1, pFile);
//
//
//  
//  //  if ( fHeader.bfType != 2 && fHeader.bfType != 3 )
//  //  {
//		//// we have not loaded the proper type of image
//  //      g_kernel->LPrintf( ("**Error Loading image: " + file).c_str() );
//  //      g_kernel->LPrintf( "**Improper image code: %d", fHeader.bfType );
//
//  //      fclose( pFile );
//		//return false;
//  //  }
//   
//    Image* image = new Image;
//    image->width    = iHeader.biWidth;
//	image->height   = iHeader.biHeight;
//	image->bitDepth = iHeader.biBitCount;
//    image->nBPP     = 3;
//
//    // Format our image
//	image->subWidth     = float(image->width / cols); // get subimage dimensions
//	image->subHeight    = float(image->height / rows);
//    image->id           = ++m_idcounter;
//	image->name         = file;
//
//    // set dimensions for subimages
//    image->rows     = rows;
//    image->columns  = cols;
//
//    //move file point to the begging of bitmap data
//    fseek(pFile, fHeader.bfOffBits, SEEK_SET);
//
//    //allocate enough memory for the bitmap image data
//    image->data = new unsigned char[ iHeader.biSizeImage ];
//
//    //verify memory allocation
//    if (!image->data)
//    {
//        image->Delete();
//        fclose(pFile);
//        return false;
//    }
//
//    //read in the bitmap image data
//    fread( image->data, iHeader.biSizeImage, 1, pFile);
//    unsigned char swap = 0;
//    for (int start = 0, end = iHeader.biSizeImage-1; start < iHeader.biSizeImage / 2; start++, end--)
//	{
//        swap = image->data[ start];
//        image->data[ start ] = image->data [ end ];
//        image->data[ end ] = swap;
//    }
//
//    unsigned char colorSwap = 0;
//    char red = 0, green =0 , blue = 0;
//    // change BGR to RGB so OpenGL can use the data
//    for (int imageIdx = 0; imageIdx < iHeader.biSizeImage; imageIdx += image->nBPP)
//	{
//		colorSwap = image->data[imageIdx];
//        
//        blue  = image->data[ imageIdx ];
//        green = image->data[ imageIdx+1 ];
//        red   = image->data[ imageIdx+2 ];
//        //if ( image->nBPP == 4 && blue == 255 && red == 255 && green == 0 )     // if we have alpha
//        //{
//        //    image->data[ imageIdx + 3 ] = 0;   
//        //} //else if ( image->nBPP == 4 )
//           // image->data[ imageIdx + 3 ] = 255; 
//		image->data[imageIdx] = image->data[imageIdx+2];
//		image->data[imageIdx + 2] = colorSwap;
//	}
//
//    // Generate the texture
//    GenTexture( image );
//	// delete the memory
//	image->Delete();
//    return true;
//}